#![allow(dead_code, unused)]

use std::{
    ffi::*, ptr::{null, null_mut, copy, copy_nonoverlapping}, sync::{Arc, Mutex}
};
use ash::vk::{self, Extent3D, Handle};
use getset::{CloneGetters, CopyGetters, Getters, MutGetters, Setters, WithSetters};
use crate::engine::{njm_buffer::{NjmBuffer, DEFAULT_MAP_OFFSET, DEFAULT_MAP_SIZE, DEFAULT_MIN_OFFSET_ALIGN}, njm_device::NjmDevice, njm_img::NjmImg};
use std::collections::HashMap;



pub struct TexImgBingding {
    pub tex_name: String,
    pub img_name: String,
}

pub type id_t = String;
pub type Map = HashMap<id_t, NjmTex>;

#[derive(Getters, Setters, WithSetters, MutGetters, CopyGetters, CloneGetters)]
pub struct NjmTex {
    #[getset(get = "pub")]
    dev: Arc<Mutex<NjmDevice>>,

    #[getset(get = "pub")]
    img: Arc<NjmImg>,

    #[getset(get = "pub")]
    name: String,

    #[getset(get = "pub")]
    id: id_t,

    #[getset(get = "pub")]
    h_img: vk::Image,

    #[getset(get = "pub")]
    h_mem: vk::DeviceMemory,

    #[getset(get = "pub")]
    h_img_view: vk::ImageView,

    #[getset(get = "pub")]
    h_sampler: vk::Sampler,   
}

impl NjmTex {
    // 
    const DEFAULT_IMAGE_TYPE: vk::ImageType = vk::ImageType::TYPE_2D;
    const DEFAULT_IMAGE_VIEW_TYPE: vk::ImageViewType = vk::ImageViewType::TYPE_2D;
    const DEFAULT_IMAGE_USAGE_1: vk::ImageUsageFlags = vk::ImageUsageFlags::TRANSFER_DST;
    const DEFAULT_IMAGE_USAGE_2: vk::ImageUsageFlags = vk::ImageUsageFlags::SAMPLED;
    const DEFAULT_IMAGE_LAYOUT: vk::ImageLayout = vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL;
    const DEFAULT_IMAGE_ASPECT: vk::ImageAspectFlags = vk::ImageAspectFlags::COLOR;
    const DEFAULT_IMAGE_TILING: vk::ImageTiling = vk::ImageTiling::OPTIMAL;

    // init
    pub fn new(dev: Arc<Mutex<NjmDevice>>, name: &str, img: Arc<NjmImg>) -> Self {
        let (h_img, h_mem) = Self::create_tex_img(dev.clone(), img.clone());
        let h_view = Self::create_tex_view(dev.clone(), img.clone(), h_img);
        let h_sampler = Self::create_tex_sampler(dev.clone());

        return Self {
            dev: dev.clone(),
            img: img.clone(),
            name: name.to_owned(),
            id: name.to_owned(),
            h_img: h_img,
            h_mem: h_mem,
            h_img_view: h_view,
            h_sampler: h_sampler,
        };
    }
    // init helper
    pub fn create_tex_img(dev: Arc<Mutex<NjmDevice>>, img: Arc<NjmImg>) -> (vk::Image, vk::DeviceMemory) {
        let mut stage_buf = NjmBuffer::new(dev.clone(), img.data().len() as vk::DeviceSize, 1, 
                vk::BufferUsageFlags::TRANSFER_SRC, 
                vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, DEFAULT_MIN_OFFSET_ALIGN);
        stage_buf.map(DEFAULT_MAP_SIZE, DEFAULT_MAP_OFFSET);
        stage_buf.write_to_buffer(img.data().as_ptr() as *const c_void, DEFAULT_MAP_SIZE, DEFAULT_MAP_OFFSET);
        stage_buf.flush(DEFAULT_MAP_SIZE, DEFAULT_MAP_OFFSET);

        let img_info = vk::ImageCreateInfo::default()
                .image_type(Self::DEFAULT_IMAGE_TYPE)
                .extent(Extent3D {
                    width: *img.width(),
                    height: *img.height(),
                    depth: 1,
                })
                .mip_levels(1)
                .array_layers(1)
                .format(img.get_vk_format())
                .tiling(Self::DEFAULT_IMAGE_TILING)
                .initial_layout(vk::ImageLayout::UNDEFINED)
                .usage(Self::DEFAULT_IMAGE_USAGE_1 | Self::DEFAULT_IMAGE_USAGE_2)
                .sharing_mode(vk::SharingMode::EXCLUSIVE)
                .samples(vk::SampleCountFlags::TYPE_1)
                .flags(vk::ImageCreateFlags::empty())
                ;

        let dev_ref = dev.clone();
        let njm_dev = dev_ref.lock().unwrap();
        let (img_vk, mem_vk) = njm_dev.create_image(img_info, vk::MemoryPropertyFlags::DEVICE_LOCAL).unwrap();
        njm_dev.copy_buffer_to_image(*stage_buf.buf(), img_vk, *img.width(), *img.height());
        return (img_vk, mem_vk);
    }

    pub fn create_tex_view(dev: Arc<Mutex<NjmDevice>>, img: Arc<NjmImg>, img_vk: vk::Image) -> vk::ImageView {
        let view_info = vk::ImageViewCreateInfo::default()
            .image(img_vk)
            .view_type(Self::DEFAULT_IMAGE_VIEW_TYPE)
            .format(img.get_vk_format())
            .subresource_range(vk::ImageSubresourceRange {
                aspect_mask: Self::DEFAULT_IMAGE_ASPECT,
                base_mip_level: 0,
                level_count: 1,
                base_array_layer: 0,
                layer_count: 1,
            });
        let njm_dev = dev.lock().unwrap();
        let view = unsafe { njm_dev.device().create_image_view(&view_info, None).unwrap() };
        return view;
    }

    pub fn create_tex_sampler(dev: Arc<Mutex<NjmDevice>>) -> vk::Sampler {
        let sampler_info = vk::SamplerCreateInfo::default()
                .mag_filter(vk::Filter::LINEAR)
                .min_filter(vk::Filter::LINEAR)
                .address_mode_u(vk::SamplerAddressMode::REPEAT)
                .address_mode_v(vk::SamplerAddressMode::REPEAT)
                .address_mode_w(vk::SamplerAddressMode::REPEAT)
                .mipmap_mode(vk::SamplerMipmapMode::LINEAR)
                .max_anisotropy(1.0)
                .compare_op(vk::CompareOp::NEVER)
                .min_lod(0.0)
                .max_lod(0.0)
                .border_color(vk::BorderColor::INT_OPAQUE_BLACK);

        let njm_dev = dev.lock().unwrap();
        let sampler = unsafe { njm_dev.device().create_sampler(&sampler_info, None).unwrap() };
        return sampler;
    }

    // get helper
    pub fn get_extent(&self) -> vk::Extent2D {
        return self.img.get_vk_extent();
    }

    pub fn get_combined_descriptor(&self) -> vk::DescriptorImageInfo {
        return vk::DescriptorImageInfo {
            sampler: self.h_sampler,
            image_view: self.h_img_view,
            image_layout: Self::DEFAULT_IMAGE_LAYOUT,
        };
    }

    pub fn get_image_descriptor(&self) -> vk::DescriptorImageInfo {
        return vk::DescriptorImageInfo {
            sampler: vk::Sampler::null(),
            image_view: self.h_img_view,
            image_layout: Self::DEFAULT_IMAGE_LAYOUT,
        };
    }

    pub fn get_sampler_descriptor(&self) -> vk::DescriptorImageInfo {
        return vk::DescriptorImageInfo {
            sampler: self.h_sampler,
            image_view: vk::ImageView::null(),
            image_layout: Self::DEFAULT_IMAGE_LAYOUT,
        };
    }
}

impl Drop for NjmTex {
    fn drop(&mut self) {
        let njm_dev = self.dev.lock().unwrap();
        if !self.h_sampler.is_null() {
            unsafe { njm_dev.device().destroy_sampler(self.h_sampler, None) };
        }
        if !self.h_img_view.is_null() {
            unsafe { njm_dev.device().destroy_image_view(self.h_img_view, None) };
        }
        if !self.h_img.is_null() {
            unsafe { njm_dev.device().destroy_image(self.h_img, None) };
        }
        if !self.h_mem.is_null() {
            unsafe { njm_dev.device().free_memory(self.h_mem, None) };
        }
    }
}